#ifndef SOCKET_UTIL
#define SOCKET_UTIL

//#include "config.h"

#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
//_LEAN_AND_MEAN
#include <windows.h>
//#undef  WIN32_LEAN_AND_MEAN
#define socket_t SOCKET
#define socklen_t int

#else
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define socket_t int
#endif

#ifndef WIN32
#define closesocket(fd) close(fd)
#define SOCKET_ERROR  -1
#define INVALID_SOCKET -1
#endif

#ifdef WIN32
#define socket_error() WSAGetLastError()
#define set_socket_error(errcode) \
    do { WSASetLastError(errcode); } while(0)
#define ERR(e) (WSA##e)
#define SOCKOPT_T char
#define SOMAXCONN 256
int gettimeofday(struct timeval *tv, struct timezone *tz);
typedef __int64 int64_t
#else
#define socket_error() (errno)
#define set_socket_error(errcode) \
    do { errno = errcode; } while(0)
#define ERR(e) (e)
#define SOCKOPT_T void
//#define get_socket_error(sock) (errno)
#endif


#ifndef WIN32

/* True iff e is an error that means a read/write operation can be retried. */
#define ERR_RW_RETRIABLE(e)	\
	((e) == EINTR || (e) == EAGAIN)
/* True iff e is an error that means an connect can be retried. */
#define ERR_CONNECT_RETRIABLE(e) \
	((e) == EINTR || (e) == EINPROGRESS)
/* True iff e is an error that means a accept can be retried. */
#define ERR_ACCEPT_RETRIABLE(e) \
	((e) == EINTR || (e) == EAGAIN || (e) == ECONNABORTED)

/* True iff e is an error that means the connection was refused */
#define ERR_CONNECT_REFUSED(e) \
	((e) == ECONNREFUSED)

#else

#define ERR_RW_RETRIABLE(e) \
	((e) == WSAEWOULDBLOCK || (e) == WSAEINTR)

#define ERR_CONNECT_RETRIABLE(e) \
	((e) == WSAEWOULDBLOCK || (e) == WSAEINTR || (e) == WSAEINPROGRESS || (e) == WSAEINVAL)

#define ERR_ACCEPT_RETRIABLE(e)	\
	    ERR_RW_RETRIABLE(e)

#define ERR_CONNECT_REFUSED(e) \
	((e) == WSAECONNREFUSED)

#endif

/*--------------------------- common socket nonblock operations ------------------------ */
namespace Sockets
{
    enum CONN_STATE{ CONN_OK = 0, CONN_RETRIABLE, CONN_REFUSED };
    enum ACCEPT_STATE{ ACCEPT_OK = 0, ACCEPT_RETRIABLE };
    enum RW_STATE{RW_OK = 0, RW_RETRIABLE };

    socket_t createNonblockSocket();
    int connect(socket_t sockfd, const struct sockaddr_in &addr);
    int accept(socket_t sockfd, int *confd_ptr, struct sockaddr_in *in_addr);
    void listen(socket_t sockfd);
    void bind(socket_t sock, const struct sockaddr_in &addr);
    void shutdownWrite(socket_t sockfd);
    struct sockaddr_in getLocalAddr(socket_t sockfd);
    struct sockaddr_in getPeerAddr(socket_t sockfd);
    int get_readable_nbytes(socket_t sockfd);
    bool isSelfConnect(socket_t sockfd);
};
/* ---------------------------------------------------------------------------- */

int get_socket_error(socket_t sock);

const char *str_socket_error(int errcode);

int set_socket_nonblock(socket_t sock);

//allow binding the same address after we close the socket
int set_socket_reuseable(socket_t sock);


int get_addrinfo(const char *name, struct sockaddr_in *addrptr);

int util_snprintf(char *buf, size_t buflen, const char *format, ...);

#endif
